﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NetworkingLibrary.Clients.UDP;
using NetworkingLibrary.Servers.UDP;
using NetworkingLibrary.Clients.TCP;
using NetworkingLibrary.Servers.TCP;
using NetworkingLibrary.Clients.Combined;
using NetworkingLibrary.Servers.Combined;
using NetworkingLibrary.Messages;
using NetworkingLibrary.Messages.Specialised.Game;
using NetworkingLibrary.Events;
using NetworkingLibrary.Exceptions;

using System.Net;
using System.Threading;

namespace TCPUDPCombined_Test_Application
{
    class Program
    {
        static int ServerTCPPort = 56789;
        static int ServerUDPPort = 5000;
        static int Client1Port = 56788;
        static int Client2Port = 56787;

        static Random Rand = new Random();

        static int MaxConnections = 2;
        static int MulticastTTL = 1;

        static IPAddress MulticastAddress = IPAddress.Parse("224.192.6.7");
        static IPAddress LocalMachineAddress = Dns.GetHostAddresses(Dns.GetHostName()).First();

        static ManualResetEvent TestMessagesReceivedEvent = new ManualResetEvent(false);
        static int NumTestMessagesReceived = 0;
        static int NumTestMessagesSent = 0;

        static void Main(string[] args)
        {
            UDPTCPCombinedServer TheServer = new UDPTCPCombinedServer();
            TheServer.OnClientConnect += new ClientConnectDelegate(TheServer_OnClientConnect);
            TheServer.OnClientDisconnect += new ClientDisconnectDelegate(TheServer_OnClientDisconnect);
            TheServer.OnError += new ErrorDelegate(TheServer_OnError);
            TheServer.OnMaxConnections += new MaxConnectionsDelegate(TheServer_OnMaxConnections);
            TheServer.OnMessageReceived += new MessageReceivedDelegate(TheServer_OnMessageReceived);

/*TCP & Normal UDP*/    //if (TheServer.StartBoth(ServerTCPPort, ServerUDPPort, LocalMachineAddress.ToString(), MaxConnections, null, MulticastTTL))
/*TCP & Multicast UDP*/ if (TheServer.StartBoth(ServerTCPPort, ServerUDPPort, LocalMachineAddress.ToString(), MaxConnections, MulticastAddress, MulticastTTL))
/*Normal UDP Only*/     //if (TheServer.Start(ServerUDPPort, LocalMachineAddress.ToString(), MaxConnections, null, MulticastTTL))
/*Multicast UDP Only*/  //if (TheServer.Start(ServerUDPPort, null, MaxConnections, MulticastAddress, MulticastTTL))
/*TCP Only*/            //if (TheServer.Start(ServerTCPPort, LocalMachineAddress.ToString(), MaxConnections))
            {
                //Do not use any of the Reliable modes when using Multicast UDP in THIS test application - Mutlicast UDP does not work properly when 
                //having multiple clients or servers or both on one machine - only one of them picks up the packets.
                TheServer.UDPMode = NetworkingLibrary.Utilities.UDP.UDPMessagingMode.ReliableOrdered;

                Console.WriteLine("Started server.");

                UDPTCPCombinedClient CombinedClient1 = new UDPTCPCombinedClient(Client1Port);
                CombinedClient1.OnConnect += new ClientConnectDelegate(CombinedClient1_OnConnect);
                CombinedClient1.OnDisconnect += new ClientDisconnectDelegate(CombinedClient1_OnDisconnect);
                CombinedClient1.OnError += new ErrorDelegate(CombinedClient1_OnError);
                CombinedClient1.OnMessageReceived += new MessageReceivedDelegate(CombinedClient1_OnMessageReceived);

/*TCP Only*/            //if (CombinedClient1.Connect(ServerTCPPort, LocalMachineAddress, true))
/*TCP & Normal UDP*/    //if (CombinedClient1.ConnectBoth(ServerTCPPort, LocalMachineAddress, ServerUDPPort, LocalMachineAddress, true, false, MulticastTTL))
/*TCP & Multicast UDP*/ if (CombinedClient1.ConnectBoth(ServerTCPPort, LocalMachineAddress, ServerUDPPort, MulticastAddress, false, true, MulticastTTL))
/*Normal UDP Only*/     //if (CombinedClient1.Connect(ServerUDPPort, LocalMachineAddress, true, false, MulticastTTL))
/*Multicast UDP Only*/  //if (CombinedClient1.Connect(ServerUDPPort, MulticastAddress, false, true, MulticastTTL))
                {
                    CombinedClient1.UDPMode = TheServer.UDPMode;

                    UDPTCPCombinedClient CombinedClient2 = new UDPTCPCombinedClient(Client2Port);
                    CombinedClient2.OnConnect += new ClientConnectDelegate(CombinedClient2_OnConnect);
                    CombinedClient2.OnDisconnect += new ClientDisconnectDelegate(CombinedClient2_OnDisconnect);
                    CombinedClient2.OnError += new ErrorDelegate(CombinedClient2_OnError);
                    CombinedClient2.OnMessageReceived += new MessageReceivedDelegate(CombinedClient2_OnMessageReceived);

/*TCP & Normal UDP*/    //if (CombinedClient2.ConnectBoth(ServerTCPPort, LocalMachineAddress, ServerUDPPort, LocalMachineAddress, true, false, MulticastTTL))
/*TCP & Multicast UDP*/ if (CombinedClient2.ConnectBoth(ServerTCPPort, LocalMachineAddress, ServerUDPPort, MulticastAddress, false, true, MulticastTTL))
/*Normal UDP Only*/     //if (CombinedClient2.Connect(ServerUDPPort, LocalMachineAddress, true, false, MulticastTTL))
/*Multicast UDP Only*/  //if (CombinedClient2.Connect(ServerUDPPort, MulticastAddress, false, true, MulticastTTL))
                    {

                        CombinedClient2.UDPMode = TheServer.UDPMode;

                        //Flush streams before sending messages - cleans out connection stuff.
                        CombinedClient1.Send(new byte[1], false);
                        CombinedClient2.Send(new byte[1], false);
                        CombinedClient1.Send(new byte[1], true);
                        CombinedClient2.Send(new byte[1], true);
    
                        bool Terminate = false;
                        while (!Terminate)
                        {
                            Console.WriteLine();
                            Console.WriteLine();
                            Console.WriteLine("-----------------------------------------");
                            Console.WriteLine();
                            Console.WriteLine("Press enter to send test message or escape to exit.");
                            ConsoleKeyInfo Input = Console.ReadKey();
       
                            if (Input.Key == ConsoleKey.Enter)
                            {
                                TestMessage AMessage = new TestMessage(new Random().Next(), "Your TCP random number 1 is : ");
                                //Thread.Sleep(100) required otherwise the Random returns exact same number twice.
                                Thread.Sleep(200);
                                TestMessage BMessage = new TestMessage(new Random().Next(), "Your TCP random number 2 is : ");
                                Thread.Sleep(200);
                                TestMessage CMessage = new TestMessage(new Random().Next(), "Your UDP random number 1 is : ");
                                Thread.Sleep(200);
                                TestMessage DMessage = new TestMessage(new Random().Next(), "Your UDP random number 2 is : ");

                                TestMessagesReceivedEvent.Reset();
                                NumTestMessagesReceived = 0;
                                NumTestMessagesSent = 1;

                                CombinedClient1.Send(AMessage, false);
                                CombinedClient2.Send(BMessage, false);
                                CombinedClient1.Send(CMessage, true);
                                CombinedClient2.Send(DMessage, true);

                                TestMessagesReceivedEvent.WaitOne(10000);
                            }
                            else if (Input.Key == ConsoleKey.Escape)
                            {
                                Terminate = true;
                            }

                        }
                        
                        if (!CombinedClient2.Disconnect())
                        {
                            Console.WriteLine("Failed to disconnect Combined Client 2!");
                        }
                    }
                    else
                    {
                        Console.WriteLine("Failed to connect Combined Client 2 !");
                    }

                    if (!CombinedClient1.Disconnect())
                    {
                        Console.WriteLine("Failed to disconnect Combined Client 1!");
                    }
                }
                else
                {
                    Console.WriteLine("Failed to connect Combined Client 1 !");
                }

                if (!TheServer.Stop())
                {
                    Console.WriteLine("Failed to stop server!");
                }
                else
                {
                    Console.WriteLine("Stopped server.");
                }
            }
            else
            {
                Console.WriteLine("Failed to start server!");

                TheServer.Stop();
            }

            Console.WriteLine("-------------------------------------------");
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        static void TheServer_OnMessageReceived(MessageReceivedEventArgs e)
        {
            Type MessageType = e.TheMessage.GetType();
            if (MessageType == typeof(TestMessage))
            {
                TestMessage TheMessage = (TestMessage)e.TheMessage;
                Console.WriteLine("Server message receieved : " + MessageType.Name + " : ");
                Console.WriteLine(TheMessage.StringTest + " : " + TheMessage.NumberTest);
                NumTestMessagesReceived++;
                if (NumTestMessagesReceived == NumTestMessagesSent)
                {
                    TestMessagesReceivedEvent.Set();
                }
            }
        }
        static void TheServer_OnMaxConnections(MaxConnectionsEventArgs e)
        {
            Console.WriteLine("Server max connections reached.");
        }
        static void TheServer_OnError(ErrorEventArgs e)
        {
            Console.Write(e.TheException.Message);
        }
        static void TheServer_OnClientDisconnect(ClientDisconnectEventArgs e)
        {
            Console.WriteLine("Server client disconnected.");
        }
        static void TheServer_OnClientConnect(ClientConnectEventArgs e)
        {
            Console.WriteLine("Server client connected.");
        }
        
        static void CombinedClient1_OnError(ErrorEventArgs e)
        {
            Console.Write(e.TheException.Message);
        }
        static void CombinedClient1_OnDisconnect(ClientDisconnectEventArgs e)
        {
            Console.WriteLine("Combined Client 1 disconnected.");
        }
        static void CombinedClient1_OnConnect(ClientConnectEventArgs e)
        {
            Console.WriteLine("Combined Client 1 connected.");
        }
        static void CombinedClient1_OnMessageReceived(MessageReceivedEventArgs e)
        {
            Type MessageType = e.TheMessage.GetType();
            if (MessageType == typeof(TestMessage))
            {
                TestMessage TheMessage = (TestMessage)e.TheMessage;
                Console.WriteLine("Combined Client 1 message receieved : " + MessageType.Name + " : ");
                Console.WriteLine(TheMessage.StringTest + " : " + TheMessage.NumberTest);
            }
        }

        static void CombinedClient2_OnError(ErrorEventArgs e)
        {
            Console.Write(e.TheException.Message);
        }
        static void CombinedClient2_OnDisconnect(ClientDisconnectEventArgs e)
        {
            Console.WriteLine("Combined Client 2 disconnected.");
        }
        static void CombinedClient2_OnConnect(ClientConnectEventArgs e)
        {
            Console.WriteLine("Combined Client 2 connected.");
        }
        static void CombinedClient2_OnMessageReceived(MessageReceivedEventArgs e)
        {
            Type MessageType = e.TheMessage.GetType();
            if (MessageType == typeof(TestMessage))
            {
                TestMessage TheMessage = (TestMessage)e.TheMessage;
                Console.WriteLine("Combined Client 2 message receieved : " + MessageType.Name + " : ");
                Console.WriteLine(TheMessage.StringTest + " : " + TheMessage.NumberTest);
            }
        }

    }
}
